// src/stores/universalEditorStore.ts
import { defineStore } from 'pinia';
// Import ALL interfaces and types from universalFlow using 'import type'
import type {
    UniversalFlowNode,
    UniversalFlowEdge,
    SourceFormat,
    UniversalFlowNodeData, // Import if needed for specific data manipulation
    NodeSemanticType
} from '@/models/universalFlow'; // Import our universal model types
import { parseToUniversal } from '@/parsers/universalParser'; // Import the universal parser
import { calculateLayout, type LayoutOptions } from '@/services/universalLayoutService';
import { formatXmlWithOptions, type FormattingOptions } from '@/utils/formatterUtils'; // Keep for potential XML-only formatting, IMPORT FormattingOptions type
import { useUniversalVisualizerSettingsStore } from './universalVisualizerSettingsStore';
// TODO: Need a universal equivalent or adapter for generateXmlFromFlow
// import { generateXmlFromFlow } from '@/utils/xmlGenerator';
import { v4 as uuidv4 } from 'uuid';
import { ElMessage, ElMessageBox } from 'element-plus';
import { useFileManagerStore } from './fileManagerStore'; // To save/load
import { ref, computed } from 'vue';
import type { ParseUniversalResult } from '@/parsers/universalParser';
import { useSettingsStore } from './settingsStore';
import { quickJsService } from '@/services/quickjsService'; // Import QuickJS service
import { parseAndDeserializeQuickJsResult } from '@/utils/universalDeserializer';

// Interface for file info (can be reused or redefined if needed)
interface CurrentFileInfo {
    uuid: string | null;
    name: string;
    lastModified: Date | null;
    sourceFormat: SourceFormat;
    isNew: boolean;
}

// Helper function to determine format from filename
export function determineFormatFromName(filename: string): SourceFormat {
    const lowerCaseName = filename.toLowerCase();
    if (lowerCaseName.endsWith('.xml') || lowerCaseName.endsWith('.jdf')) {
        return 'xml';
    } else if (lowerCaseName.endsWith('.json')) {
        return 'json';
    } else if (lowerCaseName.endsWith('.yaml') || lowerCaseName.endsWith('.yml')) {
        return 'yaml';
    } else if (lowerCaseName.endsWith('.toml')) {
        return 'toml';
    } else if (lowerCaseName.endsWith('.js') || lowerCaseName.endsWith('.mjs')) {
        return 'javascript';
    } else if (lowerCaseName.endsWith('.ts') || lowerCaseName.endsWith('.mts')) {
        return 'typescript';
    } else {
        console.warn(`Could not determine format for: ${filename}. Defaulting to 'unknown'.`);
        return 'unknown';
    }
}

// Define the new store
export const useUniversalEditorStore = defineStore('universalEditor', {
    state: () => ({
        rawContent: '' as string,
        // Store the raw parsed document (DOM, JS Object, etc.) from universalParser if needed
        universalParseResult: {
            success: false,
            nodes: [] as UniversalFlowNode[],
            edges: [] as UniversalFlowEdge[],
            rootId: null,
            rawDocument: null,
            error: undefined,
        } as ParseUniversalResult,
        // Node IDs for UI interactions
        drawerNodeId: null as string | null,
        visualSelectedNodeId: null as string | null,
        // Information about the currently loaded file
        currentFileInfo: {
            uuid: null,
            name: 'Untitled',
            lastModified: null,
            sourceFormat: 'unknown' as SourceFormat,
            isNew: true
        } as CurrentFileInfo,
        // UI State
        isLoading: false as boolean, // Loading state for parsing/layout
        error: null as string | null, // Error messages
        isDirty: false as boolean, // Tracks unsaved changes
        evaluatedConfig: null as any, // NEW: State for QuickJS execution result
    }),

    getters: {
        /** Checks if a file is currently loaded in the editor */
        hasLoadedFile(state): boolean {
            return state.currentFileInfo.uuid !== null;
        },
        /** Checks if there is any content in the editor or a file loaded */
        hasContent(state): boolean {
            return state.rawContent.length > 0 || (state.universalParseResult.nodes?.length ?? 0) > 0;
        },
        /** Gets the currently visually selected flow node data */
        selectedFlowNode(state): UniversalFlowNode | undefined {
            return state.universalParseResult.nodes?.find(node => node.id === state.visualSelectedNodeId);
        },
        /** Gets the node data for the details drawer */
        drawerNodeData(state): UniversalFlowNode | undefined {
            return state.universalParseResult.nodes?.find(node => node.id === state.drawerNodeId);
        },
        // GETTER NAME RESTORED: Provides the layouted elements for Vue Flow
        flowElements(state): { nodes: UniversalFlowNode[], edges: UniversalFlowEdge[] } {
            return {
                nodes: state.universalParseResult.nodes,
                edges: state.universalParseResult.edges,
            };
        },
        // --- ADD: Getter for readOnly state ---
        readOnly(state): boolean {
            // Example logic: editor is read-only if no file is loaded
            // return !state.currentFileInfo.uuid;
            // --- MODIFIED: Always allow editing ---
            return false;
        },
        // --- ADD: Getter for current content (alias for rawContent) ---
        currentContent(state): string {
            return state.rawContent;
        }
    },

    actions: {
        /** Clears all editor state, resetting to initial values */
        clearEditorState() {
            console.log("[Universal Editor] Clearing state.");
            this.rawContent = '';
            this.universalParseResult = { success: false, nodes: [], edges: [], rootId: null, rawDocument: null, error: undefined };
            this.currentFileInfo = { uuid: null, name: 'Untitled', lastModified: null, sourceFormat: 'unknown', isNew: true };
            this.drawerNodeId = null;
            this.visualSelectedNodeId = null;
            this.isLoading = false;
            this.error = null;
            this.isDirty = false;
            this.evaluatedConfig = null; // Reset evaluated config
        },

        /**
         * Loads content and file info into the universal editor state.
         * Called by fileManagerStore when a file is opened.
         */
        async loadContent(content: string, fileInfo: Omit<CurrentFileInfo, 'isNew'>): Promise<boolean> {
            console.log(`[Universal Editor] Attempting to load content for ${fileInfo.name} (${fileInfo.uuid}), format: ${fileInfo.sourceFormat}`);
            this.isLoading = true;
            this.error = null;
            this.evaluatedConfig = null; // Reset evaluated config on new load
            this.drawerNodeId = null;
            this.visualSelectedNodeId = null;

            try {
                this.currentFileInfo = { ...fileInfo, isNew: !fileInfo.uuid };
                this.rawContent = content;
                const result = await parseToUniversal(content, fileInfo.sourceFormat);
                this.universalParseResult = result;

                if (!result.success) {
                    this.error = result.error || '解析失败';
                    console.error(`[Universal Editor] Parsing failed for ${fileInfo.name}: ${this.error}`);
                    ElMessage.error(`解析 ${fileInfo.name} 失败: ${this.error}`);
                    this.universalParseResult.nodes = [];
                    this.universalParseResult.edges = [];
                    this.evaluatedConfig = null;
                } else {
                    console.log(`[Universal Editor] Parsing successful. Nodes: ${result.nodes.length}, Edges: ${result.edges.length}.`);

                    // Execute script if applicable
                    if (fileInfo.sourceFormat === 'javascript' || fileInfo.sourceFormat === 'typescript') {
                        console.log(`[Universal Editor] Attempting to execute script and deserialize result via QuickJS...`);

                        // --- FIX LINTER ERROR: Using explicit type assertion --- //
                        // Check if error is truthy first (not null or empty string)
                        if (this.error) {
                            // Assert that this.error is a string here before calling startsWith
                            if ((this.error as string).startsWith('QuickJS')) {
                                console.log('[Universal Editor] Clearing previous QuickJS error.');
                                this.error = null; // Reset the state property
                            }
                        }
                        // ---------------------------------------------------- //

                        try {
                            const serializedIrJson = await quickJsService.executeScriptAndGetSerializedIr(this.rawContent);
                            const deserializedResult = parseAndDeserializeQuickJsResult(serializedIrJson);

                            // --- MODIFY LOGGING HERE --- Use direct object logging
                            console.log('[Universal Editor] Deserialized QuickJS result (evaluatedConfig):', deserializedResult);
                            // -------------------------

                            if (deserializedResult && deserializedResult.__universalExecutionError__) {
                                const qjsError = deserializedResult.__universalExecutionError__;
                                console.error(`[Universal Editor] QuickJS execution failed:`, qjsError);
                                const errorMessage = qjsError instanceof Error ? qjsError.message : String(qjsError);
                                this.error = `QuickJS Execution Error: ${errorMessage}`; // Set new error
                                this.evaluatedConfig = null;
                                ElMessage.error(`脚本执行失败: ${errorMessage}`);
                            } else {
                                console.log(`[Universal Editor] QuickJS execution and deserialization successful.`);
                                this.evaluatedConfig = deserializedResult;
                            }
                        } catch (err: any) { // Catch errors from the service call itself
                            console.error(`[Universal Editor] Error during QuickJS service interaction:`, err);
                            this.error = `QuickJS Service Error: ${err.message}`;
                            this.evaluatedConfig = null;
                            ElMessage.error(`QuickJS 服务交互错误: ${err.message}`);
                        }
                    } else {
                         this.evaluatedConfig = null;
                    }

                    console.log(`[Universal Editor] Applying layout...`);
                    this.redoLayout();
                    this.isDirty = false;
                    console.log(`[Universal Editor] Successfully loaded and processed ${fileInfo.name}`);
                    return true;
                }
            } catch (err: any) {
                this.error = `加载或处理文件时出错: ${err.message}`;
                console.error('[Universal Editor loadContent] Error:', err);
                this.universalParseResult = { success: false, nodes: [], edges: [], rootId: null, rawDocument: null, error: this.error };
                this.evaluatedConfig = null;
                ElMessage.error(this.error);
                return false;
            } finally {
                this.isLoading = false;
            }
            return false;
        },

        /**
         * Called when the text editor content changes. Updates rawContent and sets isDirty.
         */
        handleEditorChange(newContent: string) {
            if (this.isLoading) return;
            if (newContent !== this.rawContent) {
                this.rawContent = newContent;
                if (this.hasLoadedFile) {
                    this.isDirty = true;
                }
            }
        },

        // --- UI Interaction Actions ---
        selectNodeForDrawer(nodeId: string | null): void {
            this.drawerNodeId = nodeId;
            this.visualSelectedNodeId = nodeId;
        },

        selectNodeVisually(nodeId: string | null): void {
            this.visualSelectedNodeId = nodeId;
        },

        // --- File Saving Actions (Interacting with fileManagerStore) ---

        /** Requests the fileManagerStore to save the current rawContent */
        async requestSave(): Promise<boolean> {
            if (!this.hasLoadedFile || !this.currentFileInfo.uuid) {
                console.warn("[Universal Editor requestSave] Save requested but no file is loaded.");
                return false;
            }
            if (!this.isDirty) {
                console.log("[Universal Editor requestSave] No changes detected (isDirty is false). Save skipped.");
                ElMessage.info("内容未更改，无需保存。");
                return true; // Indicate success (nothing to save)
            }

            console.log(`[Universal Editor requestSave] Requesting save for UUID: ${this.currentFileInfo.uuid}`);
            const fileManager = useFileManagerStore();
            // Call fileManager to handle the DB update
            const success = await fileManager.saveEditorContent(this.currentFileInfo.uuid, this.rawContent);
            // Success/failure notification will come back via notifySaveSuccess/Failure
            return success;
        },

        /** Called by fileManagerStore after a successful save */
        notifySaveSuccess(uuid: string, timestamp: Date) {
            if (this.currentFileInfo.uuid === uuid) {
                this.isDirty = false;
                this.currentFileInfo.lastModified = timestamp; // Update timestamp
                this.error = null; // Clear previous save errors
                console.log(`[Universal Editor notifySaveSuccess] Save confirmed for ${uuid}. isDirty reset.`);
            } else {
                console.warn(`[Universal Editor notifySaveSuccess] Received success confirmation for ${uuid}, but current file is ${this.currentFileInfo.uuid}.`);
            }
        },

        /** Called by fileManagerStore after a failed save */
        notifySaveFailure(uuid: string) {
            if (this.currentFileInfo.uuid === uuid) {
                // Keep isDirty = true, error message is likely handled by fileManager
                console.error(`[Universal Editor notifySaveFailure] Save failed for ${uuid}. isDirty remains true.`);
            } else {
                console.warn(`[Universal Editor notifySaveFailure] Received failure confirmation for ${uuid}, but current file is ${this.currentFileInfo.uuid}.`);
            }
        },

        // --- TODO: Actions needing adaptation for Universal Model ---

        /** Formats content - currently only supports XML */
        async formatContent(): Promise<string | undefined> {
            const format = determineFormatFromName(this.currentFileInfo.name);
            if (format !== 'xml') {
                ElMessage.warning(`当前仅支持 XML 格式化。`);
                return this.rawContent;
            }
            if (!this.rawContent) return;

            // Get formatting options from the settings store
            const settingsStore = useSettingsStore();
            const options: FormattingOptions = settingsStore.formattingOptions;

            let formatted: string | undefined = undefined;
            try {
                formatted = formatXmlWithOptions(this.rawContent, options);
                if (formatted !== this.rawContent) {
                    this.rawContent = formatted;
                    this.error = null;
                    if (this.hasLoadedFile) {
                        this.isDirty = true;
                        await this.requestSave();
                    }
                }
                return formatted;
            } catch (err: any) {
                console.error('[Universal Editor formatContent] Error:', err);
                this.error = `格式化时发生意外错误: ${err.message}`;
                ElMessage.error(this.error);
                return this.rawContent;
            }
        },

        /** Syncs changes from the visual graph back to the rawContent (editor) */
        async syncVisualChangesToEditor(): Promise<boolean> {
            // This needs a way to generate JS/TS from the evaluatedConfig + potentially manual changes?
            // Very complex, likely out of scope for now.
            console.error("syncVisualChangesToEditor needs a universal implementation (JS/TS generation)");
            ElMessage.error("尚未实现从可视化同步回 JS/TS 编辑器的功能。");
            return false;
        },

        /** Reparses editor content and updates the visualizer */
        async applyEditorChangesToVisualizer(): Promise<boolean> {
            // This should re-run loadContent essentially
            console.log("[Universal Editor] Applying editor changes to visualizer by reloading content...");
            if (!this.currentFileInfo) {
                ElMessage.error("无法应用更改：未加载文件信息。",);
                return false;
            }
            // Re-trigger parsing, QuickJS execution (if applicable), and layout
            const success = await this.loadContent(this.rawContent, this.currentFileInfo);
            return success;
        },

        // --- REVISED: Layout Action using Service ---
        redoLayout() {
            console.log("[Universal Editor redoLayout] Recalculating layout via service..."); // Revert log message
            // Ensure we have nodes to work with (Revert to simpler check)
            if (!this.universalParseResult.nodes || this.universalParseResult.nodes.length === 0) {
                console.log("[Universal Editor redoLayout] No nodes to layout.");
                return;
            }
            this.isLoading = true;
            this.error = null;

            // Keep original nodes/edges reference (optional, good practice)
            const originalNodes = this.universalParseResult.nodes;
            const originalEdges = this.universalParseResult.edges;

            try {
                // --- Step 1: Prepare nodes for layout service (Revert to original logic) ---
                // Pass only non-hidden nodes and edges between them
                const nodesToLayout = originalNodes.filter(n => !n.hidden);
                const nodeIdsToLayout = new Set(nodesToLayout.map(n => n.id));
                const edgesToLayout = originalEdges.filter(e =>
                    nodeIdsToLayout.has(e.source) && nodeIdsToLayout.has(e.target)
                );

                if (nodesToLayout.length === 0) {
                     console.log("[Universal Editor redoLayout] No visible nodes to send to layout service.");
                     // If no nodes are visible, just ensure state reflects this (no hidden changes)
                     this.universalParseResult.nodes = originalNodes;
                     this.universalParseResult.edges = originalEdges;
                     this.isLoading = false;
                     return;
                 }

                // --- Step 2: Call Layout Service (Unchanged) ---
                const vizSettings = useUniversalVisualizerSettingsStore();
                const options: LayoutOptions = {
                    algorithm: 'dagre',
                    direction: vizSettings.layoutDirection,
                    nodeSpacing: vizSettings.nodeSpacing,
                    layerSpacing: vizSettings.layerSpacing,
                    align: vizSettings.dagreAlign,
                    ranker: vizSettings.dagreRanker
                };
                console.log(`[Universal Editor redoLayout] Calling calculateLayout with ${nodesToLayout.length} nodes and ${edgesToLayout.length} edges.`); // Revert log
                const layoutResult = calculateLayout(nodesToLayout, edgesToLayout, options);
                console.log(`[Universal Editor redoLayout] Layout service returned ${layoutResult.nodes.length} nodes.`); // Revert log

                // --- Step 3: Apply results (Revert to simpler logic) ---
                // Create a map for quick lookup
                const layoutedNodeMap = new Map<string, { position: { x: number; y: number } }>();
                layoutResult.nodes.forEach(n => {
                    if (n.position) {
                        layoutedNodeMap.set(n.id, { position: n.position });
                    }
                });

                // Apply positions back to the original node list
                // We modify the existing array to maintain reactivity if needed, or create new
                this.universalParseResult.nodes = originalNodes.map(node => {
                    const layoutInfo = layoutedNodeMap.get(node.id);
                    if (layoutInfo) {
                        // Node was laid out, update position and ensure not hidden
                        return { ...node, position: layoutInfo.position, hidden: false };
                    } else {
                        // Node was not laid out (was originally hidden), keep its state
                        return { ...node, hidden: true }; // Ensure hidden is true if not laid out
                    }
                });
                // Keep original edges
                this.universalParseResult.edges = originalEdges;

                console.log(`[Universal Editor redoLayout] Layout applied successfully to state.`); // Revert log

            } catch (err: any) {
                this.error = `Layout calculation failed: ${err.message}`; // Revert error message
                console.error('[Universal Editor redoLayout] Error:', err);
                ElMessage.error(this.error);
                // Restore original nodes/edges on error
                this.universalParseResult.nodes = originalNodes;
                this.universalParseResult.edges = originalEdges;
            } finally {
                this.isLoading = false;
            }
        },

        // REVISED: setDagreDirection now simply triggers redoLayout
        setDagreDirection(direction: 'TB' | 'LR') {
             console.log(`[Universal Editor setDagreDirection] Requesting direction change to: ${direction}`);
             // Update the setting in the visualizer store
             const vizSettings = useUniversalVisualizerSettingsStore();
             vizSettings.setLayoutDirection(direction);
             // Re-apply layout, which will pick up the new direction from vizSettings
             this.redoLayout();
        },

        // --- TODO: Adapt Node Manipulation Actions ---
        // These actions need significant changes as they depend heavily on the node data structure
        // and the semantics of adding/deleting/modifying in different formats.

        toggleNodeExpansion(nodeId: string): void {
            console.warn("toggleNodeExpansion needs review for UniversalFlowNode");
            // Comment out lines causing Linter errors and referencing removed state
            // const node = this.flowElements.nodes.find(n => n.id === nodeId);
            // if (!node || !node.data.childrenIds || node.data.childrenIds.length === 0) return;
            // if (node.data.isExpanded === undefined) node.data.isExpanded = true;
            // node.data.isExpanded = !node.data.isExpanded;
            // ... (complex logic)
            // this._triggerReactivityAndUpdateLayout(false);
        },
        expandAllChildren(nodeId: string): void { console.warn("expandAllChildren not implemented for universal model"); },
        collapseAllChildren(nodeId: string): void { console.warn("collapseAllChildren not implemented for universal model"); },
        updateNodeAttribute(payload: { nodeId: string; oldName: string; newName: string; newValue: string }): Promise<void> { console.warn("updateNodeAttribute needs review/update for universal model"); return Promise.resolve(); },
        addAttribute(payload: { nodeId: string; name: string; value: string }): Promise<void> { console.warn("addAttribute needs review/update for universal model"); return Promise.resolve(); },
        deleteAttribute(payload: { nodeId: string; attributeName: string }): Promise<void> { console.warn("deleteAttribute needs review/update for universal model"); return Promise.resolve(); },
        async deleteNodeWithDescendants(nodeIdToDelete: string): Promise<void> {
            // Ensure nodes array exists before checking
            const nodeExists = this.universalParseResult.nodes?.some(n => n.id === nodeIdToDelete);
            if (!nodeExists) {
                console.warn(`[Universal Editor deleteNode] Node ${nodeIdToDelete} not found.`);
                return;
            }

            const nodesToRemove = new Set<string>();
            const edgesToRemove = new Set<string>();
            const queue: string[] = [nodeIdToDelete];
            nodesToRemove.add(nodeIdToDelete);

            // Ensure edges array exists before filtering
            const edges = this.universalParseResult.edges || [];

            while (queue.length > 0) {
                const currentId = queue.shift()!;
                const outgoingEdges = edges.filter(e => e.source === currentId);
                outgoingEdges.forEach(edge => {
                    if (!edgesToRemove.has(edge.id)) {
                        edgesToRemove.add(edge.id);
                        const targetId = edge.target;
                        if (!nodesToRemove.has(targetId)) {
                            nodesToRemove.add(targetId);
                            queue.push(targetId);
                        }
                    }
                });
                const incomingEdges = edges.filter(e => e.target === currentId);
                incomingEdges.forEach(edge => edgesToRemove.add(edge.id));
            }

             // Ensure nodes array exists before filtering
             const originalNodeCount = this.universalParseResult.nodes?.length ?? 0;
             this.universalParseResult.nodes = this.universalParseResult.nodes?.filter(n => !nodesToRemove.has(n.id)) ?? [];
             this.universalParseResult.edges = edges.filter(e => !edgesToRemove.has(e.id));

            if (this.drawerNodeId && nodesToRemove.has(this.drawerNodeId)) {
                 this.selectNodeForDrawer(null); // Use action
            }
            if (this.visualSelectedNodeId && nodesToRemove.has(this.visualSelectedNodeId)) {
                 this.visualSelectedNodeId = null;
            }

             // Use nullish coalescing for safety
             if ((this.universalParseResult.nodes?.length ?? 0) < originalNodeCount) {
                 if (!this.currentFileInfo.isNew) {
                     this.isDirty = true;
                     console.log("[Universal Editor deleteNode] Marked as dirty after deletion.");
                 }
             }
        },
        addChildNode(payload: { parentId: string; newNodeName: string }): Promise<void> { console.warn("addChildNode needs review/update for universal model"); ElMessage.error("添加子节点功能尚未适配通用模型。"); return Promise.resolve(); },

        // --- Layout Helper (Keep as is, likely works) ---
        _triggerReactivityAndUpdateLayout(forceLayout: boolean = false) {
            if (forceLayout) {
                console.log("[Universal Editor] Recalculating layout via helper (needs review)... This might be redundant now.");
                this.redoLayout(); // Just call redoLayout directly if needed
            }
        },

    } // End actions
});
